package cn.lyjuan.payment.third.base.pay.chl.impl.weixin.util;

import cn.lyjuan.base.util.*;
import cn.lyjuan.payment.third.base.pay.chl.impl.weixin.bean.*;
import cn.lyjuan.payment.third.base.pay.chl.impl.weixin.enums.WxTradeType;
import cn.lyjuan.payment.third.base.pay.cst.PayConst;
import cn.lyjuan.payment.third.base.pay.cst.WXPayConstants;
import cn.lyjuan.payment.third.base.pay.enums.SimpleChlEnum;
import cn.lyjuan.payment.third.base.pay.vo.req.PayReq;
import cn.lyjuan.payment.third.base.pay.vo.req.TransferReq;
import cn.lyjuan.payment.third.base.pay.vo.res.BaseRes;
import cn.lyjuan.payment.third.base.pay.vo.res.PayRes;
import cn.lyjuan.payment.third.base.pay.vo.res.TransferRes;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.UUID;

import static cn.lyjuan.payment.third.base.pay.cst.WXPayConstants.DOMAIN_API;

/**
 * Created by ly on 2015/1/27.
 */
@Component
@Slf4j
public class WxpaySubmit {

    /**
     * 微信平台成功标识
     */
    public static final String SUCC = "SUCCESS";
    /**
     * 微信平台失败标识
     */
    public static final String FAIL = "FAIL";

    /**
     * 统一下单请求地址
     */
    public static final String TRADE_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    /**
     * 转账地址
     */
    private static final String TRANS_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";

    /**
     * 封装商家的交易请求信息
     *
     * @param params
     * @return
     */
    public static WxPayReq pkgPayReq(Map<String, String> params) {
        WxPayReq req = new WxPayReq();
        req.setTradeType(WxTradeType.parse(params.get("tradeType")));
        req.setGoodsId(0L);
        req.setNoncestr(RandomUtils.uuid());

        return req;
    }

    /**
     * 检查转账参数是否合法
     *
     * @param params 参数
     * @return BaseRes
     */
    public static BaseRes checkTransferReq(WxTransferReq params) {
        if (params.isCheckName() && StringUtils.isNull(params.getRealName())) {
            return new BaseRes(false);
        }
        return new BaseRes();
    }

    /**
     * 创建微信手机网页支付授权
     *
     * @param order
     * @param cnf
     * @return
     */
    public static String createTradeAuth(PayReq<WxPayReq> order, WxChlCnf cnf) {
        Map<String, String> params = createTradeAuthParam(order, cnf);

        // 32位 MD5 签名字符串
        String signStr = WxUtils.sortParamForSign(params);

        // 根据密钥进行 MD5 签名
        String sign = DigestUtils.md5LowerCase(signStr + "&key=" + cnf.getKey(),
                PayConst.CHARSET);
        params.put("sign", sign.toUpperCase());
        // 拼接参数为XML
        String xml = WxUtils.sortParamXml(params);

        return xml;
    }

    private static Map<String, String> createTradeAuthParam(PayReq<WxPayReq> order, WxChlCnf cnf) {
        Map<String, String> params = new HashMap<String, String>();

        params.put("appid", cnf.getAppId());// 公众号  //微信支付分配的公众账号ID（企业号corpid即为此appId）
        params.put("mch_id", cnf.getMchId());// 商户号
        params.put("nonce_str", UUID.randomUUID().toString().replaceAll("-", ""));
        params.put("body", order.getGoods());// 商品信息
        params.put("out_trade_no", order.getLocalNo());// 商户订单号
        params.put("total_fee", Integer.toString(order.getAmt()));// 金额，分为单位
        //params.put("spbill_create_ip", "");//终端IP
        params.put("notify_url", order.getNotifyUrl());// 通知地址
        params.put("trade_type", order.getParams().getTradeType().getValue());
        params.put("spbill_create_ip", "101.68.67.166");
//		params.put("openid", baseSer.getSession(SessionConstant.WXUSER_SESSION) + "");// 微信用户OPENID
//      params.put("product_id", Long.toString(order.getParams().getGoodsId()));

        return params;
    }

    /**
     * 微信交易授权
     *
     * @param tradeAuthXml
     * @return
     */
    public static Map<String, String> sendTradeAuth(String tradeAuthXml) {
        // 提交支付预请求
        String result = null;
        try {
            result = HttpUtils.sendPost(TRADE_URL, tradeAuthXml, false, PayConst.CHARSET, null);
            System.out.println("请求参数++++++++++++++++++++++++》" + tradeAuthXml);
            System.out.println("返回参数++++++++++++++++++++++++》" + result);
        } catch (Exception e) {
            log.error("wx send trade auth error", e);
            Map<String, String> params = new HashMap<>(2);
            params.put("return_code", "FAIL");
            params.put("return_msg", e.getMessage());
            return params;
        }

        // 解析结果
        return parseAuthTradeRes(result);
    }

    /**
     * 创建微信手机网页支付交易信息
     *
     * @param order 交易信息
     * @param cnf   支付渠道配置信息
     * @param auth  订单号
     * @return
     */
    public static PayRes<WxPayRes> createTradeExec(PayReq<WxPayReq> order, WxChlCnf cnf, Map<String, String> auth) {
        // 判断是否成功
        if (!SUCC.equalsIgnoreCase(auth.get("return_code"))
                || !SUCC.equalsIgnoreCase(auth.get("result_code"))) {
            return new PayRes(false, auth.get("err_code") + ":" + auth.get("return_msg"));
        }

        WxPayRes res = new WxPayRes();

        res.put("appid", cnf.getAppId());
        res.put("partnerid", cnf.getMchId());
        res.put("prepayid", auth.get("prepay_id"));
        res.put("package", "Sign=WXPay");
        res.put("noncestr", UUID.randomUUID().toString().replaceAll("-", ""));
        res.put("timestamp", System.currentTimeMillis() / 1000 + "");
        if (auth.get("trade_type").equals("NATIVE")) {
            res.put("code_url", auth.get("code_url"));
        }
        String signStr = WxUtils.sortParamForSign(res);
        String sign = DigestUtils.md5LowerCase(signStr + "&key=" + cnf.getKey(), PayConst.CHARSET);
        res.put("sign", sign.toUpperCase());

        return new PayRes<>(order.getLocalNo(), res);
    }

    /**
     * 转账
     *
     * @param order   参数
     * @param cnf     配置
     * @param tmpEnum chl
     * @return TransferRes<WxPayRes>
     */
    public static TransferRes<WxPayRes> transferExec(TransferReq<WxTransferReq> order, WxChlCnf cnf, SimpleChlEnum tmpEnum) {
        TransfersDto model = new TransfersDto();
        // 申请商户号的 app id 或商户号绑定的 app id
        model.setMch_appid(cnf.getAppId());
        // 商户号
        model.setMchid(cnf.getMchId());
        // 设备号
        String deviceInfo = order.getParams().getDeviceInfo();
        model.setDevice_info(deviceInfo == null ? "" : deviceInfo);
        // 随机字符串
        String nonce = order.getParams().getNoncestr();
        model.setNonce_str(getRandomNonce(nonce));
        // 商户订单号
        model.setPartner_trade_no(order.getLocalNo());
        // 用户openid
        model.setOpenid(order.getIdentity());
        // 校验用户姓名选项
        if (order.getParams().isCheckName()) {
            model.setCheck_name("FORCE_CHECK");
        } else {
            model.setCheck_name("NO_CHECK");
        }
        // 收款用户姓名
        String userName = order.getParams().getRealName();
        model.setRe_user_name(userName == null ? "" : userName);
        // 金额
        model.setAmount(order.getAmt() / 100.0);
        // 备注
        String remark = order.getRemark();
        model.setDesc(remark == null ? "" : remark);
        // Ip地址
        String ip = order.getParams().getIp();
        model.setSpbill_create_ip(ip == null ? "" : ip);
        // 签名
        model.setSign(WxUtils.sign(cnf.getKey(), model.map()));
        String path = cnf.getCertPath() + tmpEnum.name().toLowerCase() + "/apiclient_cert.p12";
        ResultEntity resultEntity = WxUtils.doTransfers(TRANS_URL, path, model, 3);
        if (resultEntity.isSuccess()) {
            WxPayRes res = new WxPayRes();
            res.put("msg", resultEntity.getMsg());
            return new TransferRes<>(order.getLocalNo(), res);
        }
        return new TransferRes<>(false,resultEntity.getMsg());
    }

    /**
     * 作用：统一下单<br>
     * 场景：公共号支付、扫码支付、APP支付
     *
     * @param connectTimeoutMs 连接超时时间，单位是毫秒
     * @param readTimeoutMs    读超时时间，单位是毫秒
     * @param isSandbox        isSandbox
     * @return API返回数据
     * @throws Exception e
     */
    public static PayRes<WxPayRes> unifiedOrder(PayReq<WxPayReq> order, WxChlCnf cnf, int connectTimeoutMs, int readTimeoutMs, SimpleChlEnum tmpEnum, boolean isSandbox) {
        String reqBody = createTradeAuth(order, cnf);
        String uri;
        if (isSandbox) {
            uri = WXPayConstants.SANDBOX_UNIFIEDORDER_URL_SUFFIX;
        } else {
            uri = WXPayConstants.UNIFIEDORDER_URL_SUFFIX;
        }
        String certPath = cnf.getCertPath() + tmpEnum.name().toLowerCase() + "/apiclient_cert.p12";
        String respXml = WXPayUtil.requestOnce(DOMAIN_API, uri, cnf.getMchId(), certPath, reqBody, connectTimeoutMs, readTimeoutMs, true);
        if (respXml == null) {
            throw new RuntimeException("wechat request can't get response");
        }
        try {
            Map<String, String> res = processResponseXml(respXml, cnf.getKey(), WXPayConstants.SignType.MD5);
            WxPayRes res2 = new WxPayRes();
            res2.putAll(res);
            return new PayRes<>(order.getLocalNo(), res2);
        } catch (Exception e) {
            log.debug("handle wechat request response error: [xml to map error] [{}]", e.getMessage());
        }
        return null;
    }

    /**
     * 处理 HTTPS API返回数据，转换成Map对象。return_code为SUCCESS时，验证签名。
     *
     * @param xmlStr API返回的XML格式数据
     * @return Map类型数据
     * @throws Exception
     */
    public static Map<String, String> processResponseXml(String xmlStr, String key, WXPayConstants.SignType signType) throws Exception {
        String RETURN_CODE = "return_code";
        String return_code;
        Map<String, String> respData = WXPayUtil.xmlToMap(xmlStr);
        if (respData == null) {
            throw new Exception("xmlToMap error");
        }
        if (respData.containsKey(RETURN_CODE)) {
            return_code = respData.get(RETURN_CODE);
        } else {
            throw new Exception(String.format("No `return_code` in XML: %s", xmlStr));
        }
        if (return_code.equals(WXPayConstants.FAIL)) {
            log.error("wechat request error: response is fail: [{}]", respData);
            return null;
        } else if (return_code.equals(WXPayConstants.SUCCESS)) {
            if (isResponseSignatureValid(respData, key, signType)) {
                return respData;
            } else {
                throw new Exception(String.format("Invalid sign value in XML: %s", xmlStr));
            }
        } else {
            throw new Exception(String.format("return_code value %s is invalid in XML: %s", return_code, xmlStr));
        }
    }

    /**
     * 判断xml数据的sign是否有效，必须包含sign字段，否则返回false。
     *
     * @param reqData 向wxpay post的请求数据
     * @return 签名是否有效
     * @throws Exception
     */
    public static boolean isResponseSignatureValid(Map<String, String> reqData, String key, WXPayConstants.SignType signType) {
        // 返回数据的签名方式和请求中给定的签名方式是一致的
        return WXPayUtil.isSignatureValid(reqData, key, signType);
    }

    /**
     * 解析微信手机网页授权结果
     *
     * @param result
     * @return
     */
    private static Map<String, String> parseAuthTradeRes(String result) {
        Map<String, String> params = new HashMap<String, String>();

        params.put("return_code", XmlUtils.getMember(result, "return_code"));
        params.put("return_msg", XmlUtils.getMember(result, "return_msg"));
        params.put("appid", XmlUtils.getMember(result, "appid"));
        params.put("mch_id", XmlUtils.getMember(result, "mch_id"));
        params.put("device_info", XmlUtils.getMember(result, "device_info"));
        params.put("nonce_str", XmlUtils.getMember(result, "nonce_str"));
        params.put("sign", XmlUtils.getMember(result, "sign"));
        params.put("result_code", XmlUtils.getMember(result, "result_code"));
        params.put("err_code", XmlUtils.getMember(result, "err_code"));
        params.put("err_code_des", XmlUtils.getMember(result, "err_code_des"));
        params.put("trade_type", XmlUtils.getMember(result, "trade_type"));
        params.put("prepay_id", XmlUtils.getMember(result, "prepay_id"));
        params.put("code_url", XmlUtils.getMember(result, "code_url"));

        return params;
    }

    public static String getRandomNonce(String nonce) {
        if (nonce != null && nonce.length() > 0) {
            return nonce;
        }
        String asd = String.valueOf(System.currentTimeMillis());
        int asd2 = new Random().nextInt(100000) + 100000;
        return asd2 + asd;
    }
}
